raw_vault
層:完成DV 2.0資料模型跟之前兩層不同的地方是,這層的設定使用的是+materialized: incremental
models:
...
automate_dv:
...
raw_vault:
+materialized: incremental
+schema: automate_dv
這個設定代表每一次執行到這一層時,dbt會按照表的唯一性約束(uniqueness constraint)的設定自動去除重複項與疊加資料。在一般情況下,通常是按照唯一鍵或時間軸的設定。而在DV 2.0,幾個重要實體標的唯一性約束主要是通過之前提到的散列值來實現,而應該是:
hub
) :實體的唯一鍵,命名約定是<table_name>_PK
link
):雙向外鍵合併後轉化成唯一鍵,命名約定是<table1_name>_<table2_name>_PK
sat
) :實際唯一性約束應是對應中心表的唯一鍵<table_name>_PK
+ 散列差HASHDIFF
。從邏輯上來看唯一鍵 + 最大導入時間LOAD_DATETIME
算是”最後一筆有效紀錄“(last effective record)。dbt在這項處理上其實有很多特別的功能,但由於AutomateDV會自動處理,我們這裡就不對AutomateDV如何實現做解釋。
hub
)設定由於大部分的散列值轉換與設定已在stage
層完成,這裡主要只是實際映射表列的模板化代碼:
models/automate_dv/raw_vault/adv__hub_opportunity.sql
{%- set source_model = "adv__stg_salesforce_opportunities" -%}
{%- set src_pk = "OPPORTUNITY_PK" -%}
{%- set src_nk = "OPPORTUNITYID" -%}
{%- set src_ldts = "LOAD_DATETIME" -%}
{%- set src_source = "RECORD_SOURCE" -%}
{{ automate_dv.hub(src_pk=src_pk, src_nk=src_nk, src_ldts=src_ldts,
src_source=src_source, source_model=source_model) }}
可以看出來,這個表主要只是對應Opportunities這個實體的索引表,對應了散列值鍵OPPORTUNITY_PK
和商業鍵OPPORTUNITYID
。
link
)設定連結表主要的作用就是按照商業邏輯在兩個實體中維持映射(mapping)。回到我們一開始討論過的DV設計,Opportunities這個實體只有一個邏輯鏈接到Accounts,所以也只需要一個鏈接表。
models/automate_dv/raw_vault/adv__link_opportunity_account.sql
{%- set source_model = "adv__stg_salesforce_opportunities" -%}
{%- set src_pk = "OPPORTUNITY_ACCOUNT_PK" -%}
{%- set src_fk = ["OPPORTUNITY_PK", "ACCOUNT_PK"] -%}
{%- set src_ldts = "LOAD_DATETIME" -%}
{%- set src_source = "RECORD_SOURCE" -%}
{{ automate_dv.link(src_pk=src_pk, src_fk=src_fk, src_ldts=src_ldts,
src_source=src_source, source_model=source_model) }}
在設計上這個表也是相對簡單的。直接定義唯一鍵OPPORTUNITY_ACCOUNT_PK
和兩個外鍵OPPORTUNITY_PK
、ACCOUNT_PK
就好了。雖然理論上來說,這個表的設計甚至可以再進一步簡化而拿掉複合唯一鍵。但這樣在實際執行物化是的資料比對步驟會需要多一倍,反而得不償失。
sat
)表在DV設計上,衛星表包含了相應中心表的描述性資料列,所以通常也是最“寬”的表。
models/automate_dv/raw_vault/adv__sat_opportunity.sql
{%- set yaml_metadata -%}
source_model: "adv__stg_salesforce_opportunities"
src_pk: "OPPORTUNITY_PK"
src_hashdiff:
source_column: "OPPORTUNITY_HASHDIFF"
alias: "HASHDIFF"
src_payload:
- "AMOUNT"
- "PROJECT_NAME"
- "OPPORTUNITY_NAME"
- "STAGE"
- "CLOSE_DATE"
src_eff: "EFFECTIVE_FROM"
src_ldts: "LOAD_DATETIME"
src_source: "RECORD_SOURCE"
{%- endset -%}
{% set metadata_dict = fromyaml(yaml_metadata) %}
{{ automate_dv.sat(src_pk=metadata_dict["src_pk"],
src_hashdiff=metadata_dict["src_hashdiff"],
src_payload=metadata_dict["src_payload"],
src_eff=metadata_dict["src_eff"],
src_ldts=metadata_dict["src_ldts"],
src_source=metadata_dict["src_source"],
source_model=metadata_dict["source_model"]) }}
這裡可以看出來,主要的payload資料就是提到的敘述性資料列。這裡的唯一性約束除了唯一鍵以外,在邏輯上也會考慮到散列差。當散列差一樣時,為了維持資料稀疏性(data sparsity)就不會寫入新的資料紀錄。
值得一提的是,在複雜的資料模型情況下,可能會有多個衛星表對應一個中心表來實現對應同一個實體的資料源,但不會有多個對應一個實體的中心表。
寫到這裡,希望讀者對DV的設計與實現有一定的幫助!接下來會稍微在DV的使用方式做解釋,還有討論適合使用的案例。
對 dbt 或 data 有興趣 :wave:?歡迎加入 dbt community 到 #local-taipei 找我們,也有實體 Meetup 請到 dbt Taipei Meetup 報名參加